package pacBioSimple;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;

public class ConstructSeedByMapping {
	
	
	String fileMapping=null;
	String fileReads=null;
	
	public ConstructSeedByMapping(String filenameMapping,String filenameReads)
	{
		this.fileMapping=filenameMapping;
		this.fileReads=filenameReads;
	}
		
	public void run()
	{
		try
		{
			BufferedReader bufferReaderMapping = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(this.fileMapping))));
			BufferedReader bufferReaderRead = new BufferedReader(new FileReader(this.fileReads));
			
			//now we have to read line by line
			int chr_name=1;
			int counter_reads=1;
			List<MappingObject> mapping_result_per_read= new ArrayList<MappingObject>();
			PacBioReadContainer read=null;
			if(bufferReaderRead.ready())
			{
				read = new PacBioReadIntoContainer().readIntoContainer(bufferReaderRead);
			}
			
			while(bufferReaderMapping.ready())
			{
				//get the mapping
				String line_mapping =bufferReaderMapping.readLine();
				MappingObject a = new MappingObject();
				a.extractMapping(line_mapping);
				//check if we have enter a new chrm
				if(a.getSeqID()>chr_name)
				{
					//correct possible seed position
					//check for overlap
					if(mapping_result_per_read.size()>0)
					{
						correctPossibleSeedPostion(mapping_result_per_read,read);
						//write the result
					}
					mapping_result_per_read.clear();
					while(counter_reads<a.getSeqID() && bufferReaderRead.ready())
					{
						read=new PacBioReadIntoContainer().readIntoContainer(bufferReaderRead);
						++counter_reads;
					}
				}
				else
				{
					mapping_result_per_read.add(a);
				}
			}
			
		}
		catch (Exception e) 
		{
			e.printStackTrace();
		}
		
		
	}
	
	
	
	private void correctPossibleSeedPostion(List<MappingObject>mapping_result, PacBioReadContainer read)
	{
		if(mapping_result.size()==1)
		{
			correctRegion(mapping_result,mapping_result.get(0).getBeginPosition(), mapping_result.get(0).getEndPosition()+1,false,read); //no overlapp -> directly correction
		}
		else
		{
			List<Integer>region = new ArrayList<Integer>();
			for(MappingObject a:mapping_result)
			{
				region.add(a.getBeginPosition());
				region.add(a.getEndPosition());
			}
			Collections.sort(region);
			
			for(int i=0; i <=region.size()-2;++i)
			{
				if(mapping_result.size()==1)
				{
					break;
				}
				List<MappingObject> overlap_region=new ArrayList<MappingObject>();
				for(int j=0;j<mapping_result.size();++j)
				{
					if(mapping_result.get(j).getBeginPosition()<=region.get(i)&&mapping_result.get(j).getEndPosition()>=region.get(i+1) ||mapping_result.get(j).getEndPosition()==region.get(i))
					{
						overlap_region.add(mapping_result.get(j));
					}
					else
					{
						break;
					}
				}
				if(overlap_region.size()==1)
				{
					//just take all the values until the <end region //last element problem!!
					correctRegion(mapping_result,region.get(i), region.get(i+1),false,read);
				}
				else
				{
					correctRegion(mapping_result,region.get(i), region.get(i+1),true,read);
					//here we have to take the majority 
				}
			}
		}
	}
		
	
	private void correctRegion(List<MappingObject> mapping_result,int begin, int end, boolean overlap, PacBioReadContainer read)
	{
		if(read.getCorrectedSequence()==null)
		{
			read.setCorrectedSequence(read.getOrginalSequence());
		}
		
		if(overlap)
		{
			Map<Integer, HashMap<BitSet, Integer> > overlapMap = new HashMap<Integer, HashMap<BitSet,Integer>>();
			for(MappingObject mapResult:mapping_result)
			{
				for(Map.Entry<Integer,BitSet>entry:mapResult.getMapping_result().entrySet())
				{
					if(entry.getKey()>=end)
					{
						break;
					}
					else
					{
						//add it
						MyUtil.addToDoubleMap(overlapMap, entry.getKey(), entry.getValue());
						mapResult.getMapping_result().remove(entry.getKey());
					}
				}
			}
			
			//now we have to each entry separately
			for(Map.Entry<Integer, HashMap<BitSet, Integer>> mapEntry :overlapMap.entrySet())
			{
				if(mapEntry.getValue().size()==1)
				{
					for(Map.Entry<BitSet,Integer> innerMapEntry:mapEntry.getValue().entrySet())
					{
						if(innerMapEntry.getValue()!=1)
						{
							//do the correction
							String val=new String();
							MyUtil.convertBitToDimer(innerMapEntry.getKey(), val);
							intigrateMapResult(val, mapEntry.getKey(), read);
						}
					}
				}
				if(mapEntry.getValue().size()>1)
				{
					Pair<Integer,BitSet>candidate =new Pair<Integer, BitSet>();
					candidate.setFirst(mapEntry.getKey());
					int counts=1;
					for(Map.Entry<BitSet,Integer> innerMapEntry:mapEntry.getValue().entrySet())
					{
						//majority wins
						if(innerMapEntry.getValue()>counts)
						{
							counts=innerMapEntry.getValue();
							candidate.setSecond(innerMapEntry.getKey());
						}
						else if(innerMapEntry.getValue()==counts)
						{
							counts=1;
						}
					}
					if(counts!=1)
					{
						String val=new String();
						MyUtil.convertBitToDimer(candidate.getSecond(), val);
						intigrateMapResult(val, mapEntry.getKey(), read);
					}					
				}	
			}
		}
		else
		{
			for(Map.Entry<Integer, BitSet> entry: mapping_result.get(0).getMapping_result().entrySet())
			{
				if(entry.getKey()>=end)
				{
					break;
				}
				else
				{
					String val=new String();
					MyUtil.convertBitToDimer(entry.getValue(), val);
					intigrateMapResult(val, entry.getKey(),read);
					//delete entry
					mapping_result.get(0).getMapping_result().remove(entry.getKey());
					
				}
			}
		}
		
		
		
	}
	
	private void intigrateMapResult(String val, Integer key, PacBioReadContainer read)
	{
		if(val.contains("-"))
		{
			if(val.charAt(0)=='-')
			{
				//insertion
				read.getCodingSeqeunce().insert(key+read.getFrameshift(),val.charAt(1));
				read.setFrameshift(read.getFrameshift()+1);
			}
			else
			{
				//deletion
				read.getCodingSeqeunce().deleteCharAt(key+read.getFrameshift());
				read.setFrameshift(read.getFrameshift()-1);
			}
		}
		else
		{
			//mismatch
			read.getCodingSeqeunce().setCharAt(key+read.getFrameshift(),val.charAt(1));
		}

	}
	
//	
//	private boolean checkforOverlap(MappingObject a, MappingObject b)
//	{
//		if(a.getEndPosition()>=b.getBeginPosition())
//		{
//			return true;
//		}
//		else return false;
//	}
	
	
	

}
